/*
XBaseTools
Copyright (C) 2008 Matthew Perry

This library/program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This libary/program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.IO;
using System.Collections.Generic;
using BaseTools.Configuration;
using BaseTools.Configuration.Parsers;

namespace BaseTools.Helpers
{
	/// <summary>
	/// A path position
	/// </summary>
	public enum PathInfo
	{
		/// <summary>
		/// Absolute path
		/// </summary>
		Absolute,
		/// <summary>
		/// Network path
		/// </summary>
		Network,
		/// <summary>
		/// Relative path
		/// </summary>
		Relative
	}
	
	/// <summary>
	/// Data returned by protocolify
	/// </summary>
	public class ProtocolInfo
	{
		/// <summary>
		/// The protocol
		/// </summary>
		public string Protocol
		{
			get
			{
				return protocol;
			}
		}
		
		private string protocol;
		
		/// <summary>
		/// The path
		/// </summary>
		/// 
		public string Path
		{
			get
			{
				return path;
			}
		}
		
		private string path;
		
		/// <summary>
		/// Creates a new ProtocolInfo
		/// </summary>
		/// <param name="url">The url</param>
		public ProtocolInfo(string url)
		{
			if (url.Contains("://"))
			{
				string[] prot = url.Split(new string[] {"://"}, 2, StringSplitOptions.None);
				
				this.protocol = prot[0];
				this.path = prot[1];
			}
			else
			{
				this.protocol = "";
				this.path = url;
			}
		}
		
		/// <summary>
		/// Creates a new ProtocolInfo
		/// </summary>
		/// <param name="protocol">The name of the protocol</param>
		/// <param name="path">The name of the path</param>
		public ProtocolInfo(string protocol, string path)
		{
			this.protocol = protocol;
			this.path = path;
		}
		
		/// <summary>
		/// The default IConfig used for this Protocol
		/// </summary>
		/// <returns></returns>
		public IConfig DefaultConfig
		{
			get
			{
				return IOHelper.DefaultProtocol(this.protocol);
			}
		}
	}
	
	/// <summary>
	/// Helper class for IO related stuff
	/// </summary>
	public class IOHelper : SharedTag
	{
		private static int ptfrm = (int) Environment.OSVersion.Platform;
		
		private static bool IsWin = !((ptfrm == 4) || (ptfrm == 6) || (ptfrm == 128));
		private static string sSlash = IsWin ? "\\" : "/", sSlashR = IsWin ? "/" : "\\";
		private static char cSlash = IsWin ? '\\' : '/', cSlashR = IsWin ? '/' : '\\';
		
		/// <summary>
		/// The OS type. True is Windows. False is Unix.
		/// </summary>
		public static bool OSType
		{
			get
			{
				return IsWin;
			}
		}
		
		
		/// <summary>
		/// Get the protocol split from the path
		/// </summary>
		/// <param name="url">The url</param>
		/// <returns>The protocol and path split</returns>
		public static ProtocolInfo Protocolify(string url)
		{
			if (url.Contains("://"))
			{
				string[] prot = url.Split(new string[] {"://"}, 2, StringSplitOptions.None);
				
				return new ProtocolInfo(prot[0], prot[1]);
			}
			else
			{
				return new ProtocolInfo("", url);
			}
		}
		
		
		/// <summary>
		/// Gets the default IConfig used for the protocol
		/// </summary>
		/// <param name="protocol">The name of the protocol</param>
		/// <returns>The corresponding IConfig or null</returns>
		public static IConfig DefaultProtocol(ProtocolInfo protocol)
		{
			return DefaultProtocol(protocol.Protocol);
		}
		
		/// <summary>
		/// Gets the default IConfig used for the protocol
		/// </summary>
		/// <param name="protocol">The name of the protocol</param>
		/// <returns>The corresponding IConfig or GeneralConfig if invalid</returns>
		public static IConfig DefaultProtocol(string protocol)
		{
			switch(protocol)
			{
				case "source":
					return new SourceConfig();
				case "general":
					return new GeneralConfig();
				case "ini":
					return new IniIConfig();
				default:
					return new GeneralConfig();
			}
		}
		
		/// <summary>
		/// Clean a path
		/// </summary>
		/// <param name="path">The path to clean</param>
		/// <returns>A clean version of path</returns>
		public static string CleanPath(string path)
		{
			string newPath = path.Replace(cSlashR, cSlash).TrimEnd(new char[] {cSlash});
			if (newPath.StartsWith("\\") && !newPath.StartsWith("\\\\") && IsWin)
				newPath = newPath.Substring(1);
			else if (newPath.StartsWith("." + sSlash))
				newPath = newPath.Substring(2);
			
			return newPath;
		}
		
		/// <summary>
		/// Checks that the letter is a valid drive letter
		/// </summary>
		/// <param name="letter">The letter to check</param>
		/// <returns>Whether letter is a valid drive letter</returns>
		public static bool IsDriveLetter(string letter)
		{
			switch(letter.ToLower())
			{
				case "a":
				case "b":
				case "c":
				case "d":
				case "e":
				case "f":
				case "g":
				case "h":
				case "i":
				case "j":
				case "k":
				case "l":
				case "m":
				case "n":
				case "o":
				case "p":
				case "q":
				case "r":
				case "s":
				case "t":
				case "u":
				case "v":
				case "w":
				case "x":
				case "y":
				case "z":
					return true;
				default:
					return false;
			}
		}
		
		/// <summary>
		/// Calculates the path's position
		/// </summary>
		/// <param name="path">The path</param>
		/// <returns>The path's postition</returns>
		public static PathInfo PathPosition(string path)
		{
			string cpath = CleanPath(path);
			
			if (cpath.StartsWith("\\\\"))
				return PathInfo.Network;
			else if (!IsWin && cpath.StartsWith("/"))
				return PathInfo.Absolute;
			else if (cpath.Length >= 2 && (IsDriveLetter(cpath[0].ToString()) && cpath[1] == ':'))
				return PathInfo.Absolute;
			else
				return PathInfo.Relative;
			
		}
		
		/// <summary>
		/// List the directories
		/// </summary>
		/// <param name="directory">The directory to look in</param>
		/// <returns>A list of the directory names inside the directory</returns>
		public static List<string> ListDirectories(string directory)
		{
			List<string> lst = new List<string>();
			DirectoryInfo di = new DirectoryInfo(directory);
			DirectoryInfo[] dirs = di.GetDirectories();
			foreach(DirectoryInfo dir in dirs)
			{
				lst.Add(dir.Name);
			}
			
			return lst;
		}
		
		/// <summary>
		/// List the files
		/// </summary>
		/// <param name="directory">The directory to look in</param>
		/// <param name="filter">The file filter</param>
		/// <returns>A list of the file names inside the directory</returns>
		public static List<string> ListFiles(string directory, string filter)
		{
			List<string> lst = new List<string>();
			
			DirectoryInfo di = new DirectoryInfo(directory);
			
			FileInfo[] files = di.GetFiles(filter);
			
			foreach(FileInfo f in files)
			{
				lst.Add(f.Name);
			}
			
			return lst;
		}
		
		/// <summary>
		/// Calculate a\b, removing redundant slashes
		/// </summary>
		/// <param name="a">The first path</param>
		/// <param name="b">The second path</param>
		/// <returns>a\b without redundant slashes</returns>
		public static string RelativeTo(string a, string b)
		{
			a = CleanPath(a);
			b = CleanPath(b);
			
			if (a.Length == 0)
				return b;
			else
				return a + sSlash + b;
		}
	}
}
